#include "stm32f10x.h"
#include "OLED_Font.h"
#include "math.h"

/*引脚配置*/
#define OLED_W_SCL(x)		GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)(x))
#define OLED_W_SDA(x)		GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)(x))

unsigned char OLED_GRAM[128][8];


/*引脚初始化*/
void OLED_I2C_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	OLED_W_SCL(1);
	OLED_W_SDA(1);
}

/**
  * @brief  I2C开始
  * @param  无
  * @retval 无
  */
void OLED_I2C_Start(void)
{
	OLED_W_SDA(1);
	OLED_W_SCL(1);
	OLED_W_SDA(0);
	OLED_W_SCL(0);
}

/**
  * @brief  I2C停止
  * @param  无
  * @retval 无
  */
void OLED_I2C_Stop(void)
{
	OLED_W_SDA(0);
	OLED_W_SCL(1);
	OLED_W_SDA(1);
}

/**
  * @brief  I2C发送一个字节
  * @param  Byte 要发送的一个字节
  * @retval 无
  */
void OLED_I2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for (i = 0; i < 8; i++)
	{
		OLED_W_SDA(Byte & (0x80 >> i));
		OLED_W_SCL(1);
		OLED_W_SCL(0);
	}
	OLED_W_SCL(1);	//额外的一个时钟，不处理应答信号
	OLED_W_SCL(0);
}

/**
  * @brief  OLED写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void OLED_WriteCommand(uint8_t Command)
{
	OLED_I2C_Start();
	OLED_I2C_SendByte(0x78);		//从机地址
	OLED_I2C_SendByte(0x00);		//写命令
	OLED_I2C_SendByte(Command); 
	OLED_I2C_Stop();
}

/**
  * @brief  OLED写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void OLED_WriteData(uint8_t Data)
{
	OLED_I2C_Start();
	OLED_I2C_SendByte(0x78);		//从机地址
	OLED_I2C_SendByte(0x40);		//写数据
	OLED_I2C_SendByte(Data);
	OLED_I2C_Stop();
}

/**
  * @brief  OLED设置光标位置
  * @param  Y 以左上角为原点，向下方向的坐标，范围：0~7
  * @param  X 以左上角为原点，向右方向的坐标，范围：0~127
  * @retval 无
  */
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
	OLED_WriteCommand(0xB0 | Y);					//设置Y位置
	OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4));	//设置X位置低4位
	OLED_WriteCommand(0x00 | (X & 0x0F));			//设置X位置高4位
}

/**
  * @brief  OLED清屏
  * @param  无
  * @retval 无
  */
void OLED_Clear(void)
{  
	uint8_t i, j;
	for (j = 0; j < 8; j++)
	{
		OLED_SetCursor(j, 0);
		for(i = 0; i < 128; i++)
		{
			OLED_WriteData(0x00);
		}
	}
	for(i=0;i<8;i++)
	{
		for(j=0;j<128;j++)
		{
			 OLED_GRAM[j][i]=0;
		}
	}
}

void OLED_Column_Clear(uint8_t Column1,uint8_t Column2)
{  
	uint8_t i, j;
	for(i=2;i<8;i++)
	{
		for(j=Column1;j<(Column2+1);j++)
		{
			OLED_SetCursor(i,j);OLED_WriteData(0x00);
		}
	}
	for(i=0;i<8;i++)
	{
		for(j=0;j<128;j++)
		{
			 OLED_GRAM[j][i]=0;
		}
	}
}

/**
  * @brief  OLED显示一个字符
  * @param  Line 行位置，范围：1~4
  * @param  Column 列位置，范围：1~16
  * @param  Char 要显示的一个字符，范围：ASCII可见字符
  * @retval 无
  */
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{      	
	uint8_t i;
	OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);		//设置光标位置在上半部分
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i]);			//显示上半部分内容
	}
	OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);	//设置光标位置在下半部分
	for (i = 0; i < 8; i++)
	{
		OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]);		//显示下半部分内容
	}
}

/**
  * @brief  OLED显示字符串
  * @param  Line 起始行位置，范围：1~4
  * @param  Column 起始列位置，范围：1~16
  * @param  String 要显示的字符串，范围：ASCII可见字符
  * @retval 无
  */
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i++)
	{
		OLED_ShowChar(Line, Column + i, String[i]);
	}
}

/**
  * @brief  OLED次方函数
  * @retval 返回值等于X的Y次方
  */
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y--)
	{
		Result *= X;
	}
	return Result;
}

/**
  * @brief  OLED显示数字（十进制，正数）
  * @param  Line 起始行位置，范围：1~4
  * @param  Column 起始列位置，范围：1~16
  * @param  Number 要显示的数字，范围：0~4294967295
  * @param  Length 要显示数字的长度，范围：1~10
  * @retval 无
  */
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
	}
}

/**
  * @brief  OLED显示数字（十进制，带符号数）
  * @param  Line 起始行位置，范围：1~4
  * @param  Column 起始列位置，范围：1~16
  * @param  Number 要显示的数字，范围：-2147483648~2147483647
  * @param  Length 要显示数字的长度，范围：1~10
  * @retval 无
  */
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
	uint8_t i;
	uint32_t Number1;
	if (Number >= 0)
	{
		OLED_ShowChar(Line, Column, '+');
		Number1 = Number;
	}
	else
	{
		OLED_ShowChar(Line, Column, '-');
		Number1 = -Number;
	}
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
	}
}

/**
  * @brief  OLED显示数字（十六进制，正数）
  * @param  Line 起始行位置，范围：1~4
  * @param  Column 起始列位置，范围：1~16
  * @param  Number 要显示的数字，范围：0~0xFFFFFFFF
  * @param  Length 要显示数字的长度，范围：1~8
  * @retval 无
  */
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i, SingleNumber;
	for (i = 0; i < Length; i++)							
	{
		SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
		if (SingleNumber < 10)
		{
			OLED_ShowChar(Line, Column + i, SingleNumber + '0');
		}
		else
		{
			OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
		}
	}
}

/**
  * @brief  OLED显示数字（二进制，正数）
  * @param  Line 起始行位置，范围：1~4
  * @param  Column 起始列位置，范围：1~16
  * @param  Number 要显示的数字，范围：0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度，范围：1~16
  * @retval 无
  */
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
	}
}

// Parameters     : x,y -- 起始点坐标(x:0~127, y:0~7); N:汉字在.h中的索引
// Description    : 显示ASCII_8x16.h中的汉字,16*16点阵
void OLED_ShowCN(uint8_t y, uint8_t x, uint8_t N)
{
	uint8_t wm=0;
	uint16_t  adder=32*N;
	OLED_SetCursor(y , x);
	for(wm = 0;wm < 16;wm++)
	{
		OLED_WriteData(F16x16[adder]);
		adder += 1;
	}
	OLED_SetCursor(y+1,x);
	for(wm = 0;wm < 16;wm++)
	{
		OLED_WriteData(F16x16[adder]);
		adder += 1;
	}
}

// 这是自己写的显示中文字符串的函数，要先把中文字符串“共阴——列行式——逆向输出”取字模后存入asc.h相应的位置(连续存入)
//传入参数分别为：x:起始横坐标  
//		y：纵坐标(填入0-7)  
//		begin:填入的中文字符串的第一个字在我们asc.c字库里面的序号  
//                num:我们要填写几个字
//                比如要填“测试”，取完字模存入后这两个字在字库中序号为0,1,横坐标0，纵坐标第二行，就填：x：0，y：2，begin：0，num：2
void OLED_ShowCN_STR(uint8_t y , uint8_t x , uint8_t begin , uint8_t num)
{
	uint8_t i;
	for(i=0;i<num;i++){OLED_ShowCN(y,i*16+x,i+begin);}    //OLED显示标题
}

// Parameters     : x0,y0 -- 起始点坐标(x0:0~127, y0:0~7); x1,y1 -- 起点对角线(结束点)的坐标(x1:1~128,y1:1~8)
// Description    : 显示BMP位图
void OLED_DrawBMP(uint8_t y0,uint8_t x0,uint8_t y1,uint8_t x1,uint8_t N)
{
	uint16_t j=0;
	uint8_t x,y;
 
	if(y1%8==0)
		y = y1/8;
	else
		y = y1/8 + 1;
	for(y=y0;y<y1;y++)
	{
		OLED_SetCursor(y,x0);
		for(x=x0;x<x1;x++)
		{
			OLED_WriteData(BMP[N][j++]);
		}
	}
}

void OLED_DrawPoint(uint8_t x,uint8_t y,uint8_t t)
{
	uint8_t pos,bx,temp=0;
	if(x>127||y>63)return;//超出范围了.
	pos=7-(63-y)/8;
	bx=(63-y)%8;
	temp|=1<<(7-bx);
//	if(t){OLED_SetCursor(pos,x);OLED_WriteData(temp);}
//	else{OLED_SetCursor(pos,x);OLED_WriteData(~temp);}
	if(t)OLED_GRAM[x][pos]|=temp;
	else OLED_GRAM[x][pos]&=~temp;
	OLED_SetCursor(pos,x);OLED_WriteData(OLED_GRAM[x][pos]);
}

/*========================================================
*功能描述：	在0.96Oled中画线
*参数说明：	x1 -> 绝对x1坐标		y1 -> 绝对y1坐标
*			x2 -> 绝对x1坐标		y2 -> 绝对y1坐标
*			light -> 画线/清除线
*附加说明：	无
*========================================================*/
void OLED_DrawLine(uint8_t x1,uint8_t y1,uint8_t x2,uint8_t y2,uint8_t light)
{
	uint8_t i = 0;
	//先计算增量Δy和Δx
	int8_t DeltaY = 0,DeltaX = 0;
	float k = 0,b = 0;									//考虑到斜率有小数的情况，所以b也写成浮点型
	if(x1>x2)														//保持Δx为正，方便后面使用
	{
		i = x2;x2 = x1;x1 = i;
		i = y2;y2 = y1;y1 = i;
		i = 0;
	}
	DeltaY = y2 - y1;
	DeltaX = x2 - x1;
	if(DeltaX == 0)											//斜率k不存在时的画法
	{
		if(y1<=y2)
			{
				for(;y1<=y2;y1++)
				{
					OLED_DrawPoint(x1,y1,light);
				}
			}else if(y1>y2)
			{
				for(;y2<=y1;y2++)
				{
					OLED_DrawPoint(x1,y2,light);
				}
			}
	}else if(DeltaY == 0)								//斜率k为0时的画法
	{
		for(;x1<=x2;x1++)
		{
			OLED_DrawPoint(x1,y1,light);
		}	
	}else																//斜率正常存在时的画法
	{
		k = ((float)DeltaY)/((float)DeltaX);		//计算斜率
		b = y2 - k * x2;												//计算截距
		if((k>-1&k<1))
		{
			for(;x1<=x2;x1++)
			{
				OLED_DrawPoint(x1,(int)(k * x1 + b),light);
			}
		}
		else if((k>=1)|(k<=-1))
		{
			if(y1<=y2)
			{
				for(;y1<=y2;y1++)
				{
					OLED_DrawPoint((int)((y1 - b) / k),y1,light);
				}
			}else if(y1>y2)
			{
				for(;y2<=y1;y2++)
				{
					OLED_DrawPoint((int)((y2 - b) / k),y2,light);
				}
			}
		}
	}
}

void OLED_DrawCircle(uint8_t x,uint8_t y,uint8_t r) //圆心(x0,y0),半径r
{
//	uint8_t x,y;
//	for (x=x0-r;x<=x0+r;x++)
//	{
//		y=y0+sqrt(OLED_Pow(r,2)-OLED_Pow(x-x0,2));
//	
//		OLED_DrawPoint(x,y,1);
//		OLED_DrawLine(x0,y0,x,y);
//		OLED_DrawPoint(x,63-y,1);
//	}
	int8_t a,b,num;
	a=0;
	b=r;
	while(2*b>a)
	{
		OLED_DrawPoint(x+a,y-b,1);//0~1
		OLED_DrawPoint(x-a,y-b,1);//0~7
		OLED_DrawPoint(x-a,y+b,1);//4~5
		OLED_DrawPoint(x+a,y+b,1);//4~3
		OLED_DrawPoint(x+b,y+a,1);//2~3
		OLED_DrawPoint(x+b,y-a,1);//2~1
		OLED_DrawPoint(x-b,y-a,1);//6~7
		OLED_DrawPoint(x-b,y+a,1);//6~5
		a++;
		num=(a*a+b*b)-r*r;//计算画的点离圆心的距离
		if(num>0)
		{
			b--;
			a--;
		}
//		OLED_DrawLine(x,y,x+a,y-b,0);
	}
}


/**
  * @brief  OLED初始化
  * @param  无
  * @retval 无
  */
void OLED_Init(void)
{
	uint32_t i, j;
	
	for (i = 0; i < 1000; i++)			//上电延时
	{
		for (j = 0; j < 1000; j++);
	}
	
	OLED_I2C_Init();			//端口初始化
	
	OLED_WriteCommand(0xAE);	//关闭显示
	
	OLED_WriteCommand(0xD5);	//设置显示时钟分频比/振荡器频率
	OLED_WriteCommand(0x80);
	
	OLED_WriteCommand(0xA8);	//设置多路复用率
	OLED_WriteCommand(0x3F);
	
	OLED_WriteCommand(0xD3);	//设置显示偏移
	OLED_WriteCommand(0x00);
	
	OLED_WriteCommand(0x40);	//设置显示开始行
	
	OLED_WriteCommand(0xA1);	//设置左右方向，0xA1正常 0xA0左右反置
	
	OLED_WriteCommand(0xC8);	//设置上下方向，0xC8正常 0xC0上下反置

	OLED_WriteCommand(0xDA);	//设置COM引脚硬件配置
	OLED_WriteCommand(0x12);
	
	OLED_WriteCommand(0x81);	//设置对比度控制
	OLED_WriteCommand(0xCF);

	OLED_WriteCommand(0xD9);	//设置预充电周期
	OLED_WriteCommand(0xF1);

	OLED_WriteCommand(0xDB);	//设置VCOMH取消选择级别
	OLED_WriteCommand(0x30);

	OLED_WriteCommand(0xA4);	//设置整个显示打开/关闭

	OLED_WriteCommand(0xA6);	//设置正常/倒转显示

	OLED_WriteCommand(0x8D);	//设置充电泵
	OLED_WriteCommand(0x14);

	OLED_WriteCommand(0xAF);	//开启显示
		
	OLED_Clear();				//OLED清屏
}
